package nfwx.analytics;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Map;

/**
 * 应用信息采集主入口
 * <p>
 * 要求的权限有:
 * android.permission.READ_PHONE_STATE
 * android.permission.ACCESS_NETWORK_STATE
 * android.permission.ACCESS_WIFI_STATE
 * android.permission.BATTERY_STATS
 * android.permission.INTERNET
 * android.permission.READ_EXTERNAL_STORAGE
 * android.permission.CHANGE_WIFI_STATE
 * android.permission.BLUETOOTH
 * </p>
 */
public class Grab implements GrabSendQueue.SendQueueIo, Handler.Callback
{
    //应用上下文对象
    private Context _appCtx;
    //定时Handler
    private Handler _handler;
    //服务器地址
    private String _url;
    //发送队列
    private GrabSendQueue _queue;
    //设备ID
    private String _deviceId;
    //安装渠道
    private String _from;

    //应用会话
    private GrabAPPSession _appSess;

    //记录应用是否被暂停
    private boolean _appPaused;

    //队列存储名
    static final String QUEUE_NAME="grab_send_queue";

    ///单例
    static Grab _singleInstance;

    //send心跳事件
    static final int MSG_SEND_HEART_BEAT=100;
    //post心跳
    static final int MSG_APP_POST_HEART_BEAT=101;

    //应用信息提交间隔
    static final int APP_POST_DELAY=30000;

	private Grab(Context appCtx, String url, String appId, String secret, String from)
    {
        _appCtx = appCtx;
        _url = url;
        _from = from;
        _handler = new Handler(this);
        //加载设备ID
        _loadDeviceId();
        _queue = new GrabSendQueue(this, appId, secret);
        //5秒钟心跳调用send
        _handler.sendEmptyMessageDelayed(MSG_SEND_HEART_BEAT, 5000);
        setupCrashHandler();
    }

    /**
     * 初始化采集系统, 通常在Application中进行初始化
     * @param appCtx 应用上下文
     * @param url 服务器提交地址
     * @param appId 应用ID
     * @param secret 应用密钥
     * @param from 安装渠道
     */
    public static Grab initialize(Context appCtx, String url, String appId, String secret, String from)
    {
        if (_singleInstance != null) return _singleInstance;
        _singleInstance = new Grab(appCtx, url, appId, secret, from);
        return _singleInstance;
    }

    /**
     * 获取统计对象单例, 需要在调用initialize后方可调用本函数
     * @return
     */
    public static Grab getInstance()
    {
        if (_singleInstance == null) throw new RuntimeException("Grab.initialize is not called");
        return _singleInstance;
    }

    /**
     * 开启一个应用会话, 通常在Activity.onCreate创建时调用, 在主界面初始化前调用，防止在主界面初始化过程中发生了崩溃而应用会话未开启导致无法记录的问题
     * @return 应用会话
     */
    public GrabAPPSession beginAppSession()
    {
        if (_appSess != null) {
            try {
                endSession(_appSess);
            } catch(JSONException e) {
                e.printStackTrace();
            }
            _appSess = null;
        }
        _handler.removeMessages(MSG_APP_POST_HEART_BEAT);
        GrabAPPSession s = new GrabAPPSession();
        s.deviceId.setValue(_deviceId);
        s.app.id.setValue(_queue.getAppId());
        if (_from != null) {
            s.app.from.setValue(_from);
        }
        GrabAppSessionFiller.fill(s, _appCtx);
        _appSess = s;
        try {
            post(s);
        } catch(Exception e) {
            e.printStackTrace();
        }
        _handler.sendEmptyMessageDelayed(MSG_APP_POST_HEART_BEAT, APP_POST_DELAY);
        return s;
    }

    /**
     * 提交会话数据, 任何时候修改了会话数据请及时提交, 对于应用会话框架自动每30秒提交一次
     * @param session
     */
    public void post(GrabSession session) throws JSONException {
        try {
            if (!session.getChanged()) return;
            _queue.post(session);
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 结束会话, 对于应用会话，在窗口destroy时进行结束. 对于视频会话，则每次换台或者close播放器时结束之前会话
     * @param session
     */
    public void endSession(GrabSession session) throws JSONException {
        if (session.getEnded()) return; //已经结束
        if (session == _appSess) {
            _appSess = null;
            _handler.removeMessages(MSG_APP_POST_HEART_BEAT);
        }
        session.endSession();
        post(session);
    }

    /**
     * 开启一个视频会话
     * @param url 视频地址
     * @param name 视频标题
     * @return 视频会话
     */
    public GrabVideoSession beginVideoSession(String url, String name)
    {
        GrabVideoSession s = new GrabVideoSession();
        s.deviceId.setValue(_deviceId);
        s.app.id.setValue(_queue.getAppId());
        if (_from != null) {
            s.app.from.setValue(_from);
        }
        s.meta.url.setValue(url);
        if (name != null) {
            s.meta.name.setValue(name);
        }
        GrabVideoSessionFiller.fill(s, _appCtx);
        return s;
    }

    /**
     * 当应用进入后台时调用, 通常在Activity.onPause中调用
     */
    public void appPause()
    {
        _appPaused = true;
        if (_appSess != null) {
            try {
                _appSess.appPause();
                post(_appSess);
            } catch(JSONException e) {
                e.printStackTrace();
            }
            _handler.removeMessages(MSG_APP_POST_HEART_BEAT);
        }
    }

    /**
     * 当应用进入前台时调用, 通常在Activity.onResume中调用
     */
    public void appReusme()
    {
        if (!_appPaused) { //新创建的Activity也会调用onResume, 因此只有onPause了才执行
            return;
        }
        _appPaused = false;
        if (_appSess != null) {
            try {
                _appSess.appResume();
                post(_appSess);
            } catch(JSONException e) {
                e.printStackTrace();
            }
        }
        _handler.removeMessages(MSG_APP_POST_HEART_BEAT);
        _handler.sendEmptyMessageDelayed(MSG_APP_POST_HEART_BEAT, APP_POST_DELAY);
    }

    /**
     * 当应用退出时调用，通常在Activity.onDestroy中调用
     */
    public void appQuit()
    {
        _appPaused = false;
        if (_appSess != null) {
            try {
                endSession(_appSess);
            } catch(JSONException e) {
                e.printStackTrace();
            }
            _appSess = null;
        }
        _handler.removeMessages(MSG_APP_POST_HEART_BEAT);
    }

    /**
     * 停止采集对象, 应该在应用退出(Application#onDestroy)时调用
     */
    public void shutdown()
    {
        _queue.close();
        _handler.removeCallbacksAndMessages(null);
    }


    /**
     * 应用崩溃处理
     * @param code 错误码
     * @param callstack 调用堆栈
     */
    void appCrash(String code, String callstack)
    {
        if (_appSess != null) {
            _appSess.appCrash(code, callstack);
            try {
                endSession(_appSess);
            } catch(JSONException e) {
                e.printStackTrace();
            }
        }
        _queue.send();
    }

    //安装崩溃处理器
    void setupCrashHandler()
    {
        //未处理异常拦截并记录
        final Thread main = Thread.currentThread();
        final Thread.UncaughtExceptionHandler oldHandler = Thread.getDefaultUncaughtExceptionHandler();

        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                StringWriter stackTrace = new StringWriter();
                e.printStackTrace(new PrintWriter(stackTrace));
                final String callstack=stackTrace.toString();
                System.err.println(callstack);
                if (main.getId() == t.getId()) { //主线程崩溃
                    appCrash("EXCEPTION", callstack);
                } else { //其它线程崩溃
                    _handler.post(new Runnable() {
                        @Override
                        public void run() {
                            appCrash("EXCEPTION", callstack);
                        }
                    });
                    //休眠4s，等待主线程处理, 不要超出5s, 否则主线程可能因为等待本线程而导致anr
                    try {
                        Thread.sleep(4000);
                    } catch (InterruptedException e2) {
                        ;
                    }
                }
                //调用旧处理器
                if (oldHandler != null) {
                    oldHandler.uncaughtException(t, e);
                }
            }
        });
    }

    //GrabSendQueue.SendQueueIo.open 实现
    @Override
    public String open() throws IOException {
        SharedPreferences sp = _appCtx.getSharedPreferences(QUEUE_NAME, Context.MODE_PRIVATE);
        return sp.getString(QUEUE_NAME, null);
    }

    //GrabSendQueue.SendQueueIo.save 实现
    @Override
    public void save(String s) throws IOException {
        SharedPreferences sp = _appCtx.getSharedPreferences(QUEUE_NAME, Context.MODE_PRIVATE);
        sp.edit().putString(QUEUE_NAME, s).commit();
    }

    //GrabSendQueue.SendQueueIo.send 实现
    @Override
    public void send(final Map<String, String> statInfo, final GrabSendQueue.SendCallBack cb) {
        new AsyncTask<Void,Void,Boolean>(){
            @Override
            protected Boolean doInBackground(Void... params) {
                String s = GrabHttp.httpPOST(_url, statInfo);
                if (s == null) return false;
                else return true;
            }

            @Override
            protected void onPostExecute(Boolean v) {
                if (!v) {
                    cb.onFail(new IOException("Failed to send"));
                } else {
                    cb.onOK();
                }
            }
        }.execute();
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) { //发送心跳
            case MSG_SEND_HEART_BEAT:
                _queue.send();
                _handler.sendEmptyMessageDelayed(MSG_SEND_HEART_BEAT, 5000);
                break;
            case MSG_APP_POST_HEART_BEAT: //提交心跳
                if (_appSess != null) {
                    try {
                        _appSess.useTimeDelta.setValue(APP_POST_DELAY);
                        post(_appSess);
                    } catch(JSONException e) {
                        e.printStackTrace();
                    }
                }
                _handler.removeMessages(MSG_APP_POST_HEART_BEAT);
                _handler.sendEmptyMessageDelayed(MSG_APP_POST_HEART_BEAT, APP_POST_DELAY);
                break;
        }
        return false;
    }

    /**
     * 加载设备ID
     */
    private void _loadDeviceId()
    {
        //先从存储中获取
        SharedPreferences sp = _appCtx.getSharedPreferences(QUEUE_NAME, Context.MODE_PRIVATE);
        String id = sp.getString("deviceId", null);
        if (id == null || id.length() != 32) { //获取不到则产生设备ID
            id = GrabDeviceId.generateDeviceId(_appCtx);
            //保存到存储中
            sp.edit().putString("deviceId", id).commit();
        }
        _deviceId = id;
    }
}